package com.hd123.rumba.gwt.widget2.client.panel;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.logical.shared.BeforeSelectionEvent;
import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
import com.google.gwt.event.logical.shared.HasBeforeSelectionHandlers;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DeckPanel;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ListenerWrapper;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.SourcesTabEvents;
import com.google.gwt.user.client.ui.TabListener;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.hd123.rumba.gwt.util.client.MessageRecord;
import com.hd123.rumba.gwt.widget2.client.form.RContainerValidator;
import com.hd123.rumba.gwt.widget2.client.form.RValidatable;

/**
 * 标签面板
 * <p>
 * 特性：
 * <li>动态添加标签，动态加载控件
 * <li>标签的关闭按钮（可选）
 * <li>自动整合RCaptionBox
 * <li>可选的最小化按钮、最大化按钮、关闭按钮
 * <li>双击标签区域可最大化（可选）
 * <li>内容区域可设定高度并滚动
 * 
 * @author LiQi
 * 
 */
@SuppressWarnings("deprecation")
public class RTabPanel extends Composite implements HasWidgets, SourcesTabEvents,
    HasSelectionHandlers<RTabDef>, HasBeforeSelectionHandlers<RTabDef>, RValidatable {

  /** 样式 */
  private static final String STYLENAME = "rb-TabPanel";
  private static final String STYLENAME_TABBAR = "rb-TabPanel-tabbar";
  private static final String STYLENAME_TABBARITEM = "rb-TabPanel-tabbaritem";
  private static final String STYLENAME_TABBARITEM_CAPTION = "rb-TabPanel-tabbaritem-caption";
  private static final String STYLENAME_TABBARITEM_CLOSEBUTTON = "rb-TabPanel-tabbaritem-closebutton";
  private static final String STYLENAME_CONTENTROOT = "rb-TabPanel-contentroot";
  private static final String STYLENAME_SCROLLPANEL = "rb-TabPanel-scrollpanel";

  private static final String DEPENDENT_STYLENAME_SELECTED = "selected";
  private static final String DEPENDENT_STYLENAME_HOVER = "hover";
  private static final String DEPENDENT_STYLENAME_MAXIMIZE = "maximize";
  private static final String DEPENDENT_STYLENAME_MINIMIZE = "minimize";

  /** 图片 */
  private static final String IMAGE_CTABBARITEM_CLOSEBUTTON = "images/tab/tab-close.gif";
  private static final String IMAGE_CTABBARITEM_CLOSEBUTTON_HOVER = "images/tab/tab-close-hover.gif";

  private static final String IMAGE_MINIMIZEBUTTON = "images/panel/minimize.png";
  private static final String IMAGE_MAXIMIZEBUTTON = "images/panel/maximize.png";
  private static final String IMAGE_CLOSEBUTTON = "images/panel/close.png";
  private static final String IMAGE_RESTOREBUTTON = "images/panel/restore.png";

  /** 根面板 */
  private VerticalPanel root;

  /** 标签栏 */
  private HorizontalPanel tabBar;

  /** 最大化、最小化、关闭按钮 */
  private Image minimizeButton, maximizeButton, closeButton;

  /** 最外围的内容面板容器 */
  private VerticalPanel contentRoot;

  /** 滚动内容面板 */
  private SimplePanel scrollPanel;

  /** 内容切换面板 */
  private DeckPanel contentDeck;

  /** 空的内容面板，用于当前标签没有setWidget时的显示，因为DeckPanel的当前widget不能为空 */
  private HTML blankContent;

  /** 空的内容面板在contentDeck中的位置 */
  private static final int BLANK_CONTENT_INDEX = 0;

  /** 标签定义 */
  private List<RTabDef> tabDefs;

  /** 当前选中的标签 */
  private RTabDef currentTabDef = null;

  /** 是否处于最小化状态 */
  private boolean minimized = false;

  /** 是否处于最大化状态 */
  private boolean maximized = false;

  /** 用于最大化恢复的数据 */
  private String restoreMaximWidth, restoreMaximHeight, restoreMaximScrollPanelHeight;

  /** 是否允许双击最大化 */
  private boolean doubleClickToMaximize = false;

  /** 窗口改变大小事件处理 */
  private ResizeHandler windowResizeHandler;
  private HandlerRegistration windowResizeHandlerReg;

  /** 标签项事件处理 */
  private TabBarItemHandler tabBarItemHandler;

  /** 标签项标题事件处理 */
  private TabBarItemCaptionHandler tabBarItemCaptionHandler;

  /** 标签关闭按钮事件处理 */
  private TabBarItemCloseButtonHandler tabBarItemCloseButtonHandler;

  public RTabPanel() {
    tabDefs = new ArrayList();
    tabBarItemHandler = new TabBarItemHandler();
    tabBarItemCaptionHandler = new TabBarItemCaptionHandler();
    tabBarItemCloseButtonHandler = new TabBarItemCloseButtonHandler();

    root = new VerticalPanel();
    root.setStyleName(STYLENAME);

    // 顶栏，包含标签栏和按钮栏
    HorizontalPanel topBar = new HorizontalPanel();
    topBar.setWidth("100%");
    root.add(topBar);
    root.setCellHeight(topBar, "25px");

    // 标签栏
    tabBar = new HorizontalPanel();
    tabBar.setStyleName(STYLENAME_TABBAR);
    topBar.add(tabBar);

    // 标签栏和按钮栏之间的填充，用途是处理双击事件
    final TabBarFill fill = new TabBarFill();
    topBar.add(fill);
    topBar.setCellWidth(fill, "100%");
    DeferredCommand.addCommand(new Command() {
      public void execute() {
        fill.setHeight(tabBar.getOffsetHeight() + "px");
      }
    });

    // 按钮栏
    HorizontalPanel buttonBar = new HorizontalPanel();
    buttonBar.setSpacing(3);
    topBar.add(buttonBar);
    topBar.setCellHorizontalAlignment(buttonBar, HasAlignment.ALIGN_RIGHT);
    topBar.setCellVerticalAlignment(buttonBar, HasAlignment.ALIGN_BOTTOM);

    ButtonHandler buttonHandler = new ButtonHandler();

    minimizeButton = new Image(IMAGE_MINIMIZEBUTTON, 0, 0, 15, 15);
    DOM.setStyleAttribute(minimizeButton.getElement(), "cursor", "hand");
    minimizeButton.addClickHandler(buttonHandler);
    minimizeButton.addMouseOverHandler(buttonHandler);
    minimizeButton.addMouseOutHandler(buttonHandler);
    buttonBar.add(minimizeButton);
    minimizeButton.setVisible(false);

    maximizeButton = new Image(IMAGE_MAXIMIZEBUTTON, 0, 0, 15, 15);
    DOM.setStyleAttribute(maximizeButton.getElement(), "cursor", "hand");
    maximizeButton.addClickHandler(buttonHandler);
    maximizeButton.addMouseOverHandler(buttonHandler);
    maximizeButton.addMouseOutHandler(buttonHandler);
    buttonBar.add(maximizeButton);
    maximizeButton.setVisible(false);

    closeButton = new Image(IMAGE_CLOSEBUTTON, 0, 0, 15, 15);
    DOM.setStyleAttribute(closeButton.getElement(), "cursor", "hand");
    closeButton.addClickHandler(buttonHandler);
    closeButton.addMouseOverHandler(buttonHandler);
    closeButton.addMouseOutHandler(buttonHandler);
    buttonBar.add(closeButton);
    closeButton.setVisible(false);

    // 内容面板
    contentRoot = new VerticalPanel();
    contentRoot.setSpacing(0);
    contentRoot.setWidth("100%");
    contentRoot.setStyleName(STYLENAME_CONTENTROOT);
    root.add(contentRoot);

    scrollPanel = new SimplePanel();
    scrollPanel.setWidth("100%");
    scrollPanel.setStyleName(STYLENAME_SCROLLPANEL);
    contentRoot.add(scrollPanel);

    VerticalPanel contentFrame = new VerticalPanel();
    contentFrame.setWidth("100%");
    scrollPanel.setWidget(contentFrame);

    contentDeck = new DeckPanel();
    contentDeck.setWidth("100%");
    contentFrame.add(contentDeck);

    // 空的内容面板
    blankContent = new HTML();
    contentDeck.add(blankContent);

    initWidget(root);

    // 最大化状态下需要自动随窗口大小的改变而改变
    windowResizeHandler = new ResizeHandler() {
      public void onResize(ResizeEvent event) {
        fitToWindow();
      }
    };
  }

  /**
   * 添加标签定义
   * 
   * @param tabDef
   */
  public void addTabDef(RTabDef tabDef) {
    addTabDef(tabDefs.size(), tabDef);
  }

  /**
   * 添加标签定义
   * 
   * @param index
   *          插入的位置
   * @param tabDef
   */
  public void addTabDef(int index, RTabDef tabDef) {
    tabDefs.add(index, tabDef);
    tabDef.setOwner(this);
    tabDef.setIndex(index);

    buildTabBarItem(tabDef);
    refreshTabWidget(tabDef);
    tabDef.refreshVisibility();
  }

  /**
   * 构造标签栏
   * 
   */
  private void buildTabBarItem(RTabDef tabDef) {
    FocusPanel tabBarItem = new FocusPanel();
    tabBarItem.setStyleName(STYLENAME_TABBARITEM);
    tabBarItem.addClickHandler(tabBarItemHandler);
    tabBarItem.addDoubleClickHandler(tabBarItemHandler);
    tabBarItem.addMouseOverHandler(tabBarItemHandler);
    tabBarItem.addMouseOutHandler(tabBarItemHandler);
    tabBar.add(tabBarItem);
    tabDef.setTabBarWidget(tabBarItem);

    HorizontalPanel tabBarItemFrame = new HorizontalPanel();
    tabBarItem.setWidget(tabBarItemFrame);

    // 标题
    final HTML caption = new HTML();
    caption.setWordWrap(false);
    caption.setStyleName(STYLENAME_TABBARITEM_CAPTION);
    caption.addMouseDownHandler(tabBarItemCaptionHandler);
    caption.addMouseUpHandler(tabBarItemCaptionHandler);
    tabBarItemFrame.add(caption);
    tabDef.setCaptionWidget(caption);
    tabDef.refreshCaption();

    // 关闭按钮
    Image closeButton = new Image(IMAGE_CTABBARITEM_CLOSEBUTTON);
    closeButton.setStyleName(STYLENAME_TABBARITEM_CLOSEBUTTON);
    closeButton.addClickHandler(tabBarItemCloseButtonHandler);
    closeButton.addMouseOverHandler(tabBarItemCloseButtonHandler);
    closeButton.addMouseOutHandler(tabBarItemCloseButtonHandler);
    tabBarItemFrame.add(closeButton);
    tabDef.setCloseButton(closeButton);
    closeButton.setVisible(tabDef.isShowCloseButton());
  }

  /**
   * 刷新标签的内容控件
   * 
   * @param tabDef
   */
  void refreshTabWidget(RTabDef tabDef) {
    if (tabDef.getWidget() == null)
      return;

    if (contentDeck.getWidgetIndex(tabDef.getWidget()) == -1) {
      if (tabDef.getContentIndex() != -1)
        contentDeck.remove(tabDef.getContentIndex());
      contentDeck.add(tabDef.getWidget());

      tabDef.setContentIndex(contentDeck.getWidgetCount() - 1);
      if (tabDef.equals(currentTabDef))
        contentDeck.showWidget(tabDef.getContentIndex());
    }

    if (tabDef.getWidget() instanceof RCaptionBox)
      tabDef.refreshCaptionBox();
  }

  /**
   * 切换标签页
   * 
   * @param tabDef
   *          标签定义，允许为null，表示不选择任何标签
   * @return 切换标签页是否成功。只有所有
   *         {@link TabListener#onBeforeTabSelected(SourcesTabEvents, int)}
   *         都返回true才返回true。
   */
  public boolean selectTab(RTabDef tabDef) {
    if (tabDef != null && !this.equals(tabDef.getOwner()))
      return false;

    if (tabDef != null && !tabDef.isVisible())
      return false;

    // 触发beforeTabSelected事件
    if (tabDef != null) {
      BeforeSelectionEvent event = BeforeSelectionEvent.fire(this, tabDef);
      if (event != null && event.isCanceled())
        return false;
    }

    // 旧的当前标签页
    if (currentTabDef != null) {
      currentTabDef.getTabBarWidget().removeStyleDependentName(DEPENDENT_STYLENAME_SELECTED);
      currentTabDef.getCaptionWidget().removeStyleDependentName(DEPENDENT_STYLENAME_SELECTED);
    }

    // 新的当前标签页
    currentTabDef = tabDef;
    if (tabDef != null) {
      tabDef.getTabBarWidget().addStyleDependentName(DEPENDENT_STYLENAME_SELECTED);
      tabDef.getTabBarWidget().removeStyleDependentName(DEPENDENT_STYLENAME_HOVER);
      tabDef.getCaptionWidget().addStyleDependentName(DEPENDENT_STYLENAME_SELECTED);
    }

    // 内容面板
    if (tabDef == null || tabDef.getWidget() == null)
      contentDeck.showWidget(BLANK_CONTENT_INDEX);
    else
      contentDeck.showWidget(tabDef.getContentIndex());

    // 触发tabSelected事件
    if (tabDef != null) {
      SelectionEvent.fire(this, tabDef);
    }

    return true;
  }

  /**
   * 设置当前标签页
   * 
   * @param index
   *          序号
   * @return 切换标签页是否成功。只有所有
   *         {@link TabListener#onBeforeTabSelected(SourcesTabEvents, int)}
   *         都返回true才返回true。
   */
  public boolean selectTab(int index) {
    if (index < 0 || index >= tabDefs.size())
      throw new IndexOutOfBoundsException();

    RTabDef tabDef = (RTabDef) tabDefs.get(index);
    return selectTab(tabDef);
  }

  /**
   * 选择第一个可见的标签页
   * 
   * @return 切换标签页是否成功。只有所有
   *         {@link TabListener#onBeforeTabSelected(SourcesTabEvents, int)}
   *         都返回true才返回true。当所有标签页都不可见时也会返回false。
   */
  public boolean selectFirstVisibleTab() {
    List list = getVisibleTabDefs();
    if (list.size() > 0)
      return selectTab((RTabDef) list.get(0));
    else
      return false;
  }

  /**
   * @deprecated 请用{@link #addSelectionHandler}和
   *             {@link #addBeforeSelectionHandler}代替
   */
  @Deprecated
  public void addTabListener(TabListener listener) {
    WrappedTabListener.add(this, listener);
  }

  /**
   * @deprecated 请用{@link #addSelectionHandler}和
   *             {@link #addBeforeSelectionHandler}返回的
   *             {@link HandlerRegistration#removeHandler}代替
   */
  @Deprecated
  public void removeTabListener(TabListener listener) {
    WrappedTabListener.remove(this, listener);
  }

  public HandlerRegistration addSelectionHandler(SelectionHandler<RTabDef> handler) {
    return addHandler(handler, SelectionEvent.getType());
  }

  public HandlerRegistration addBeforeSelectionHandler(BeforeSelectionHandler<RTabDef> handler) {
    return addHandler(handler, BeforeSelectionEvent.getType());
  }

  /**
   * 当前标签页
   * 
   * @return
   */
  public RTabDef getCurrentTabDef() {
    return currentTabDef;
  }

  /**
   * 关闭标签页，关闭后无法恢复
   * 
   * @param tabDef
   *          标签定义
   */
  public void closeTab(RTabDef tabDef) {
    if (tabDef == null || !this.equals(tabDef.getOwner()))
      return;

    tabBar.remove(tabDef.getTabBarWidget());
    if (tabDef.getWidget() != null)
      contentDeck.remove(tabDef.getWidget());
    tabDefs.remove(tabDef);

    // 重新计算index
    for (int i = tabDef.getIndex(); i < tabDefs.size(); i++) {
      RTabDef td = (RTabDef) tabDefs.get(i);
      td.setIndex(td.getIndex() - 1);
    }

    // 重新计算contentIndex
    if (tabDef.getContentIndex() != -1)
      for (int i = 0; i < tabDefs.size(); i++) {
        RTabDef td = (RTabDef) tabDefs.get(i);
        if (td.getContentIndex() > tabDef.getContentIndex())
          td.setContentIndex(td.getContentIndex() - 1);
      }

    // 如果被关闭的是当前标签页，则重新选择新的当前标签页
    if (tabDef.equals(currentTabDef)) {
      if (tabDef.getIndex() > 0) {
        // 选中被关闭标签左边的第一个
        selectTab(tabDef.getIndex() - 1);
      } else if (tabDef.getIndex() < tabDefs.size()) {
        // 选中被关闭标签右边的第一个
        selectTab(tabDef.getIndex());
      } else {
        // 全部标签都已被关闭
        selectTab(null);
      }
    }
  }

  /**
   * 标签页定义
   * 
   * @param index
   *          标签页定义序号
   * @return
   */
  public RTabDef getTabDef(int index) {
    return tabDefs.get(index);
  }

  /**
   * 取得标签页定义。
   * 
   * @param w
   *          指定标签页所包含的Widget。
   * @return
   */
  public RTabDef getTabDef(Widget w) {
    for (RTabDef tabDef : tabDefs)
      if (tabDef.getWidget() == w)
        return tabDef;
    return null;
  }

  /**
   * 标签页定义列表
   * 
   * @return
   */
  public List getTabDefs() {
    return tabDefs;
  }

  /**
   * 可见的标签页列表
   * 
   * @return
   */
  public List getVisibleTabDefs() {
    List list = new ArrayList();
    for (Iterator i = tabDefs.iterator(); i.hasNext();) {
      RTabDef tabDef = (RTabDef) i.next();
      if (tabDef.isVisible())
        list.add(tabDef);
    }
    return list;
  }

  /**
   * 标签页的数量，包括隐藏的，不包括被关闭的。
   * 
   * @return
   */
  public int getTabCount() {
    return tabDefs.size();
  }

  /**
   * 自动生成一个RTabDef，把widget放入其中
   * 
   * @param widget
   */
  public void add(Widget widget) {
    RTabDef tabDef = new RTabDef();
    tabDef.setWidget(widget);
    addTabDef(tabDef);
  }

  public void clear() {
    tabDefs.clear();
    tabBar.clear();
    contentDeck.clear();
  }

  /**
   * 枚举每个标签所包含的控件
   * 
   */
  public Iterator iterator() {
    List widgets = new ArrayList();
    for (int i = 0; i < tabDefs.size(); i++) {
      RTabDef tabDef = (RTabDef) tabDefs.get(i);
      if (tabDef.getWidget() != null)
        widgets.add(tabDef.getWidget());
    }
    return widgets.iterator();
  }

  /**
   * 查找widget所对应的标签，关闭之
   * 
   */
  public boolean remove(Widget widget) {
    if (widget == null)
      return false;
    for (int i = 0; i < tabDefs.size(); i++) {
      RTabDef tabDef = (RTabDef) tabDefs.get(i);
      if (widget.equals(tabDef.getWidget())) {
        closeTab(tabDef);
        return true;
      }
    }
    return false;
  }

  public void clearValidResults() {
    for (Iterator i = tabDefs.iterator(); i.hasNext();) {
      Widget widget = ((RTabDef) i.next()).getWidget();
      if (widget == null)
        continue;

      if (widget instanceof RValidatable)
        ((RValidatable) widget).clearValidResults();
      else if (widget instanceof HasWidgets)
        RContainerValidator.clearValidResults((HasWidgets) widget);
    }
  }

  public boolean isValid() {
    for (Iterator i = tabDefs.iterator(); i.hasNext();) {
      Widget widget = ((RTabDef) i.next()).getWidget();
      if (widget == null)
        continue;

      if (widget instanceof RValidatable && !((RValidatable) widget).isValid())
        return false;
      else if (widget instanceof HasWidgets && !RContainerValidator.isValid((HasWidgets) widget))
        return false;
    }
    return true;
  }

  public List<MessageRecord> getInvalidMessages() {
    List result = new ArrayList();
    for (RTabDef tabDef : tabDefs) {
      Widget widget = tabDef.getWidget();
      if (widget == null)
        continue;

      // 包含原始消息的messageList
      List<MessageRecord> messageList = null;
      if (widget instanceof RValidatable)
        messageList = ((RValidatable) widget).getInvalidMessages();
      else if (widget instanceof HasWidgets)
        messageList = RContainerValidator.getInvalidMessages((HasWidgets) widget);

      // 消息包含的控件定位焦点时需要切换tab，所以根据list产生经过加工的消息的revisedMessageList
      List<MessageRecord> revisedMessageList = new ArrayList();
      if (messageList != null) {
        for (MessageRecord messageRecord : messageList) {
          revisedMessageList.add(new MessageRecordInTabPanel(messageRecord, tabDef));
        }
      }

      result.addAll(revisedMessageList);
    }
    return result;
  }

  public boolean validate() {
    if (!isVisible())
      return true;

    boolean valid = true;
    for (Iterator i = tabDefs.iterator(); i.hasNext();) {
      RTabDef tabDef = (RTabDef) i.next();
      if (!tabDef.isVisible())
        continue;

      Widget widget = tabDef.getWidget();
      if (widget == null)
        continue;

      // 记录控件可见状态，然后使控件可见，否则控件可能不验证
      boolean widgetVisible = widget.isVisible();
      if (!widgetVisible)
        widget.setVisible(true);

      // 执行验证
      if (widget instanceof RValidatable)
        valid &= ((RValidatable) widget).validate();
      else if (widget instanceof HasWidgets)
        valid &= RContainerValidator.validate((HasWidgets) widget);

      // 恢复验证前保存的可见状态
      widget.setVisible(widgetVisible);
    }
    return valid;
  }

  /**
   * 设置所有标签页的关闭按钮的可见性
   * 
   */
  public void setShowTabCloseButton(boolean showCloseButton) {
    for (Iterator i = tabDefs.iterator(); i.hasNext();) {
      RTabDef tabDef = (RTabDef) i.next();
      tabDef.setShowCloseButton(showCloseButton);
    }
  }

  /**
   * 是否显示最小化按钮，默认为不显示
   * 
   * @return
   */
  public boolean isShowMinimizeButton() {
    return minimizeButton.isVisible();
  }

  /**
   * 设置是否显示最小化按钮，默认为不显示
   * 
   * @param showMinimizeButton
   */
  public void setShowMinimizeButton(boolean showMinimizeButton) {
    minimizeButton.setVisible(showMinimizeButton);
  }

  /**
   * 是否显示最大化按钮，默认为不显示
   * 
   * @return
   */
  public boolean isShowMaximizeButton() {
    return maximizeButton.isVisible();
  }

  /**
   * 设置是否显示最大化按钮，默认为不显示
   * 
   * @param showMaximizeButton
   */
  public void setShowMaximizeButton(boolean showMaximizeButton) {
    maximizeButton.setVisible(showMaximizeButton);
  }

  /**
   * 是否显示关闭按钮，默认为不显示
   * 
   * @return
   */
  public boolean isShowCloseButton() {
    return closeButton.isVisible();
  }

  /**
   * 设置是否显示关闭按钮，默认为不显示
   * 
   * @param showCloseButton
   */
  public void setShowCloseButton(boolean showCloseButton) {
    closeButton.setVisible(showCloseButton);
  }

  /**
   * 是否允许双击最大化，默认为false
   * 
   * @return
   */
  public boolean isDoubleClickToMaximize() {
    return doubleClickToMaximize;
  }

  /**
   * 设置是否允许双击最大化，默认为false
   * 
   * @param doubleClickToMaximize
   */
  public void setDoubleClickToMaximize(boolean doubleClickToMaximize) {
    this.doubleClickToMaximize = doubleClickToMaximize;
  }

  /**
   * 是否处于最小化状态，默认为false
   * 
   */
  public boolean isMinimized() {
    return minimized;
  }

  /**
   * 设置最小化状态
   * 
   * @param minimized
   */
  public void setMinimized(boolean minimized) {
    if (this.minimized == minimized)
      return;

    if (minimized)
      doMinimize();
    else
      doRestoreMinimize();
  }

  /**
   * 如果当前没有最小化，则最小化；如果已经最小化了，则恢复。
   * 
   */
  public void minimizeOrRestore() {
    setMinimized(!isMinimized());
  }

  /**
   * 执行最小化动作
   * 
   */
  private void doMinimize() {
    minimizeButton.setUrlAndVisibleRect(IMAGE_RESTOREBUTTON, 0, 0, 15, 15);
    if (maximized)
      doRestoreMaximize();
    scrollPanel.setVisible(false);
    contentRoot.addStyleDependentName(DEPENDENT_STYLENAME_MINIMIZE);
    minimized = true;
  }

  /**
   * 执行最小化恢复动作
   * 
   */
  private void doRestoreMinimize() {
    minimizeButton.setUrlAndVisibleRect(IMAGE_MINIMIZEBUTTON, 0, 0, 15, 15);
    scrollPanel.setVisible(true);
    contentRoot.removeStyleDependentName(DEPENDENT_STYLENAME_MINIMIZE);
    minimized = false;
  }

  /**
   * 是否处于最大化状态，默认为false
   * 
   */
  public boolean isMaximized() {
    return maximized;
  }

  /**
   * 设置最大化状态
   * 
   * @param maximized
   */
  public void setMaximized(boolean maximized) {
    if (this.maximized == maximized)
      return;

    if (maximized)
      doMaximize();
    else
      doRestoreMaximize();
  }

  /**
   * 如果当前没有最大化，则最大化；如果已经最大化了，则恢复。
   * 
   */
  public void maximizeOrRestore() {
    setMaximized(!isMaximized());
  }

  /**
   * 执行最大化动作
   * 
   */
  private void doMaximize() {
    maximizeButton.setUrlAndVisibleRect(IMAGE_RESTOREBUTTON, 0, 0, 15, 15);
    if (minimized)
      doRestoreMinimize();

    scrollToTopLeft();
    Window.enableScrolling(false);
    root.addStyleDependentName(DEPENDENT_STYLENAME_MAXIMIZE);

    // 记录最大化前的宽和高
    restoreMaximWidth = DOM.getStyleAttribute(getElement(), "width");
    restoreMaximHeight = DOM.getStyleAttribute(getElement(), "height");
    restoreMaximScrollPanelHeight = DOM.getStyleAttribute(scrollPanel.getElement(), "height");

    // 调整大小
    fitToWindow();
    contentRoot.setHeight("100%");
    scrollPanel.setHeight("100%");

    windowResizeHandlerReg = Window.addResizeHandler(windowResizeHandler);

    maximized = true;
  }

  /**
   * 使窗口滚动到左上角，最大化前调用此方法，可保证最大化正常显示
   * 
   */
  private native void scrollToTopLeft()
  /*-{
    $doc.body.scrollTop = 0;
    $doc.body.scrollLeft = 0;
  }-*/;

  /**
   * 在Chrome中，如果最大化前存在滚动条，则最大化恢复后滚动条会消失，要鼠标滚轮滚动一下才会出现。
   * 本方法就是模拟这个鼠标滚动一下的动作
   * 
   */
  private native void resetScrollBar()
  /*-{
    $doc.body.scrollTop = 1;
    $doc.body.scrollTop = 0;
  }-*/;

  /**
   * 执行最大化恢复动作
   * 
   */
  private void doRestoreMaximize() {
    maximizeButton.setUrlAndVisibleRect(IMAGE_MAXIMIZEBUTTON, 0, 0, 15, 15);

    Window.enableScrolling(true);
    resetScrollBar();
    root.removeStyleDependentName(DEPENDENT_STYLENAME_MAXIMIZE);

    // 恢复最大化前的宽和高
    setWidth(restoreMaximWidth);
    setHeight(restoreMaximHeight);
    contentRoot.setHeight("");
    scrollPanel.setHeight(restoreMaximScrollPanelHeight);

    // 不再自动随窗口大小自动改变
    if (windowResizeHandlerReg != null)
      windowResizeHandlerReg.removeHandler();

    maximized = false;
  }

  /**
   * 设置内容面板高度，内容面板是整个RTabPanel的标签栏以下的部分， 如果没设置过则每个会根据标签内容自动调整高度
   * 
   * @param height
   */
  public void setContentHeight(String height) {
    scrollPanel.setHeight(height);
  }

  /**
   * 使宽和高适应整个窗口
   * 
   */
  private void fitToWindow() {
    root.setWidth(getWindowWidth() + "px");
    root.setHeight(getWindowHeight() + "px");
  }

  /**
   * 窗口宽度
   * 
   * @return
   */
  private native int getWindowWidth()
  /*-{
    return $doc.body.clientWidth;
  }-*/;

  /**
   * 窗口高度
   * 
   * @return
   */
  private native int getWindowHeight()
  /*-{
    return $doc.body.clientHeight;
  }-*/;

  /**
   * 标签项事件处理
   * 
   * @author LiQi
   * 
   */
  private class TabBarItemHandler implements ClickHandler, MouseOverHandler, MouseOutHandler,
      DoubleClickHandler {

    /** 点击时切换标签 */
    public void onClick(ClickEvent event) {
      for (Iterator i = tabDefs.iterator(); i.hasNext();) {
        RTabDef tabDef = (RTabDef) i.next();
        if (tabDef.getTabBarWidget() != null && tabDef.getTabBarWidget().equals(event.getSource())) {
          selectTab(tabDef);
          break;
        }
      }
    }

    /** 双击时最大化 */
    public void onDoubleClick(DoubleClickEvent event) {
      if (doubleClickToMaximize)
        maximizeOrRestore();
    }

    public void onMouseOver(MouseOverEvent event) {
      if (currentTabDef == null || !currentTabDef.getTabBarWidget().equals(event.getSource()))
        ((Widget) event.getSource()).addStyleDependentName(DEPENDENT_STYLENAME_HOVER);
    }

    public void onMouseOut(MouseOutEvent event) {
      if (currentTabDef == null || !currentTabDef.getTabBarWidget().equals(event.getSource()))
        ((Widget) event.getSource()).removeStyleDependentName(DEPENDENT_STYLENAME_HOVER);
    }

  }

  /**
   * 标签标题事件处理
   * 
   * @author LiQi
   * 
   */
  private class TabBarItemCaptionHandler implements MouseDownHandler, MouseUpHandler {

    /** 避免双击最大化时标题文字被选中 */
    public void onMouseDown(MouseDownEvent event) {
      DOM.setCapture(((Widget) event.getSource()).getElement());
    }

    /** 避免双击最大化时标题文字被选中 */
    public void onMouseUp(MouseUpEvent event) {
      DOM.releaseCapture(((Widget) event.getSource()).getElement());
    }
  }

  /**
   * 标签关闭按钮事件处理
   * 
   * @author LiQi
   * 
   */
  private class TabBarItemCloseButtonHandler implements ClickHandler, MouseOverHandler,
      MouseOutHandler {

    public void onClick(ClickEvent event) {
      for (Iterator i = tabDefs.iterator(); i.hasNext();) {
        RTabDef tabDef = (RTabDef) i.next();
        if (tabDef.getCloseButton() != null && tabDef.getCloseButton().equals(event.getSource())) {
          closeTab(tabDef);
          return;
        }
      }
    }

    public void onMouseOver(MouseOverEvent event) {
      if (event.getSource() instanceof Image)
        ((Image) event.getSource()).setUrl(IMAGE_CTABBARITEM_CLOSEBUTTON_HOVER);
    }

    public void onMouseOut(MouseOutEvent event) {
      if (event.getSource() instanceof Image)
        ((Image) event.getSource()).setUrl(IMAGE_CTABBARITEM_CLOSEBUTTON);
    }
  }

  /**
   * 标签栏和按钮栏之间的填充，用途是处理双击事件
   * 
   * @author LiQi
   * 
   */
  private class TabBarFill extends HTML {

    public TabBarFill() {
      sinkEvents(Event.ONDBLCLICK);
      DOM.setStyleAttribute(getElement(), "cursor", "default");
    }

    public void onBrowserEvent(Event event) {
      if (doubleClickToMaximize && DOM.eventGetType(event) == Event.ONDBLCLICK) {
        maximizeOrRestore();
        return;
      }

      super.onBrowserEvent(event);
    }
  }

  /**
   * 最大化、最小化、关闭按钮事件处理
   * 
   * @author LiQi
   * 
   */
  private class ButtonHandler implements ClickHandler, MouseOverHandler, MouseOutHandler {

    public void onClick(ClickEvent event) {
      if (event.getSource().equals(minimizeButton)) {
        minimizeOrRestore();
      } else if (event.getSource().equals(maximizeButton)) {
        maximizeOrRestore();
      } else if (event.getSource().equals(closeButton)) {
        setVisible(false);
      }
    }

    public void onMouseOver(MouseOverEvent event) {
      ((Image) event.getSource()).setVisibleRect(15, 0, 15, 15);
    }

    public void onMouseOut(MouseOutEvent event) {
      ((Image) event.getSource()).setVisibleRect(0, 0, 15, 15);
    }
  }

  /**
   * RTabPanel所包含的MessageRecord定位焦点到控件时，需要切换到控件所在的tab，
   * 所以本类改写了focusWidget方法，以完成这项工作。
   * 
   * @author LiQi
   * 
   */
  private class MessageRecordInTabPanel extends MessageRecord {

    private RTabDef tabDef;

    public MessageRecordInTabPanel(MessageRecord messageRecord, RTabDef tabDef) {
      super(messageRecord);
      this.tabDef = tabDef;
    }

    public void focusOnWidget() {
      if (getFocusWidget() != null && tabDef != null)
        RTabPanel.this.selectTab(tabDef);
      super.focusOnWidget();
    }
  }

  @Deprecated
  private static class WrappedTabListener extends ListenerWrapper<TabListener> implements
      SelectionHandler<RTabDef>, BeforeSelectionHandler<RTabDef> {

    @Deprecated
    public static void add(RTabPanel source, TabListener listener) {
      WrappedTabListener t = new WrappedTabListener(listener);
      source.addBeforeSelectionHandler(t);
      source.addSelectionHandler(t);
    }

    public static void remove(Widget eventSource, TabListener listener) {
      baseRemove(eventSource, listener, SelectionEvent.getType(), BeforeSelectionEvent.getType());
    }

    private WrappedTabListener(TabListener listener) {
      super(listener);
    }

    public void onBeforeSelection(BeforeSelectionEvent<RTabDef> event) {
      if (!getListener().onBeforeTabSelected((SourcesTabEvents) event.getSource(),
          event.getItem().getIndex())) {
        event.cancel();
      }
    }

    public void onSelection(SelectionEvent<RTabDef> event) {
      getListener().onTabSelected((SourcesTabEvents) event.getSource(),
          event.getSelectedItem().getIndex());
    }
  }

  // ************************************************************************************************
  // 调试代码

  // private void printDebugInfo() {
  // String s = "\n";
  // for (int i = 0; i < tabDefs.size(); i++) {
  // RTabDef tabDef = (RTabDef) tabDefs.get(i);
  // s += i + ", " + tabDef.getIndex() + ", " + tabDef.getContentIndex() + ", "
  // + tabDef.getCaption() + "\n";
  // }
  // GWT.log(s, null);
  // }

  // ************************************************************************************************
}
